package com.fanxuankai.boot.log.store;

import com.fanxuankai.boot.log.LogDetailService;
import com.fanxuankai.boot.log.LogInfo;
import com.fanxuankai.boot.log.autoconfigure.LogProperties;
import com.fanxuankai.commons.util.DateUtils;
import com.fanxuankai.commons.util.IdUtils;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * JDBC 存储日志
 *
 * @author fanxuankai
 */
public class JdbcLogStore implements LogStore {
    private static final String DEFAULT_TABLE_NAME = "sys_log";
    private static final List<String> DEFAULT_COLUMNS = Arrays.asList("id", "application", "resource", "resource_id",
            "url", "safety_level", "class_name", "method_name", "params", "return_value", "server_ip", "client_ip",
            "client_address", "browser", "total_time_millis", "user_id", "username", "operation_exception", "exception_detail",
            "create_time");
    private final LogProperties.JdbcStore jdbcStore;
    private final JdbcTemplate jdbcTemplate;
    private final LogDetailService logDetailService;
    private final String preparedStatementSqlValues;
    private boolean hasCustomColumns;

    public JdbcLogStore(DataSource dataSource, LogProperties logProperties, LogDetailService logDetailService) {
        this.jdbcStore = logProperties.getJdbcStore();
        this.jdbcTemplate = new JdbcTemplate(dataSource);
        this.logDetailService = logDetailService;
        List<String> allColumns = new ArrayList<>(DEFAULT_COLUMNS);
        List<String> customColumns = Optional.ofNullable(jdbcStore)
                .map(LogProperties.JdbcStore::getCustomColumns)
                .orElse(Collections.emptyList());
        allColumns.addAll(customColumns);
        if (!customColumns.isEmpty()) {
            hasCustomColumns = true;
        }
        String columns = allColumns.stream().map(s -> "`" + s + "`").collect(Collectors.joining(","));
        String values = allColumns.stream().map(s -> "?").collect(Collectors.joining(","));
        preparedStatementSqlValues = String.format("(%s) VALUES (%s)", columns, values);
    }

    @Override
    public void store(LogInfo logInfo) {
        List<Object> values = new ArrayList<>();
        values.add(IdUtils.nextId());
        values.add(logInfo.getApplication());
        values.add(logInfo.getResource());
        values.add(logInfo.getResourceId());
        values.add(logInfo.getUrl());
        values.add(logInfo.getSafetyLevel());
        values.add(logInfo.getClassName());
        values.add(logInfo.getMethodName());
        values.add(logInfo.getParams());
        values.add(logInfo.getReturnValue());
        values.add(logInfo.getServerIp());
        values.add(logInfo.getClientIp());
        values.add(logInfo.getClientAddress());
        values.add(logInfo.getBrowser());
        values.add(logInfo.getTotalTimeMillis());
        values.add(logInfo.getUserId());
        values.add(logInfo.getUsername());
        values.add(logInfo.getOperationException());
        values.add(logInfo.getExceptionDetail());
        values.add(logInfo.getCreateTime());
        if (hasCustomColumns) {
            values.addAll(logDetailService.customValues());
        }
        jdbcTemplate.update("INSERT INTO " + getTableName(logInfo) + preparedStatementSqlValues, values.toArray());
    }

    private String getTableName(LogInfo logInfo) {
        String tableName = jdbcStore.getTableName() == null ? DEFAULT_TABLE_NAME : jdbcStore.getTableName();
        return jdbcStore.getShardingDateFormat() != null
                ? tableName + "_" + DateUtils.toText(logInfo.getCreateTime(), jdbcStore.getShardingDateFormat())
                : tableName;
    }
}